/*
 * Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2012 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "SimpleFontData.h"

#include "FloatConversion.h"
#include "FloatRect.h"
#include "Font.h"
#include "FontCache.h"
#include "FontDescription.h"
#include "GlyphBuffer.h"
#include <cairo-ft.h>
#include <cairo.h>
#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H
#include <unicode/normlzr.h>
#include <wtf/MathExtras.h>

namespace WebCore {

void SimpleFontData::platformInit()
{
    if (!m_platformData.m_size)
        return;

    ASSERT(m_platformData.scaledFont());

    double ascent;
    double descent;
    double height;

#if !USE(HARFBUZZ)
    Font::setCodePath(Font::Simple); // FIXME: Implement Complex CodePath APIs.
#endif

    cairo_scaled_font_t* font = m_platformData.scaledFont();
    FT_Face face = cairo_ft_scaled_font_lock_face(font);
    TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
    if (os2) {
        // Use OS/2 table to keep compatibility with Silk porting.
        FT_Long winAscent = FT_RoundFix(FT_MulFix((os2->usWinAscent << 10), face->size->metrics.y_scale));
        FT_Long winDescent = FT_RoundFix(FT_MulFix((os2->usWinDescent << 10), face->size->metrics.y_scale));
        height = (winAscent + winDescent) >> 16;
        descent = winDescent >> 16;
        ascent = winAscent >> 16;
    } else {
        cairo_font_extents_t fontExtents;
        cairo_scaled_font_extents(m_platformData.scaledFont(), &fontExtents);

        ascent = fontExtents.ascent;
        descent = fontExtents.descent;
        height = fontExtents.height;
    }
    float lineGap = narrowPrecisionToFloat(height - ascent - descent);
    cairo_ft_scaled_font_unlock_face(font);

    m_fontMetrics.setAscent(ascent);
    m_fontMetrics.setDescent(descent);

    // Match CoreGraphics metrics.
    m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap));
    m_fontMetrics.setLineGap(lineGap);

    cairo_text_extents_t textExtents;
    cairo_scaled_font_text_extents(m_platformData.scaledFont(), "x", &textExtents);
    m_fontMetrics.setXHeight(narrowPrecisionToFloat(textExtents.height));

    cairo_scaled_font_text_extents(m_platformData.scaledFont(), " ", &textExtents);
    m_spaceWidth = narrowPrecisionToFloat(textExtents.x_advance);
    
    m_syntheticBoldOffset = 0.0;
    if (m_platformData.weight() > FontWeightNormal)
        m_syntheticBoldOffset = 1.0;
}

void SimpleFontData::platformCharWidthInit()
{
    m_avgCharWidth = 0.f;
    m_maxCharWidth = 0.f;
    initCharWidths();
}

void SimpleFontData::platformDestroy()
{
}

PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription& fontDescription, float scaleFactor) const
{
    return adoptRef(new SimpleFontData(FontPlatformData(cairo_scaled_font_get_font_face(m_platformData.scaledFont()),
        scaleFactor * fontDescription.computedSize(), fontDescription.weight(), m_platformData.syntheticOblique()),
        isCustomFont(), false));
}

bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
{
    FT_Face face = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont());

    if (!face)
        return false;

    for (int i = 0; i < length; i++) {
        if (!FT_Get_Char_Index(face, characters[i])) {
            cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont());
            return false;
        }
    }

    cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont());

    return true;
}

void SimpleFontData::determinePitch()
{
    m_treatAsFixedPitch = m_platformData.isFixedPitch();
}

FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const
{
    if (!m_platformData.size())
        return FloatRect();

    // Calculate the bounds of the glyph. The scaled font (saved in m_platformData.scaledFont())
    // has information about the
    // transformations needed on font, like scaled size or application of oblique slant.
    // Bounds for glyph helps in calculation of glyph overflow for fonts
    // where the max glyph width (extents.x_bearing + extents.width) overshoots the 
    // advance length, given by extents.x_advance.
    cairo_glyph_t cglyph = { glyph, 0, 0 };
    cairo_text_extents_t extents;
    cairo_scaled_font_glyph_extents(m_platformData.scaledFont(), &cglyph, 1, &extents);

    if (cairo_scaled_font_status(m_platformData.scaledFont()) == CAIRO_STATUS_SUCCESS)
        return FloatRect(extents.x_bearing, extents.y_bearing, extents.width, extents.height);
    return FloatRect();
}

float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
{
    ASSERT(m_platformData.scaledFont());

    cairo_glyph_t cglyph = { glyph, 0, 0 };
    cairo_text_extents_t extents;
    cairo_scaled_font_glyph_extents(m_platformData.scaledFont(), &cglyph, 1, &extents);

    float w = (float)m_spaceWidth;
    if (cairo_scaled_font_status(m_platformData.scaledFont()) == CAIRO_STATUS_SUCCESS && extents.x_advance)
        w = (float)extents.x_advance;

    return w;
}

#if USE(HARFBUZZ)
bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
{
    if (!m_combiningCharacterSequenceSupport)
        m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool>);

    WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false);
    if (!addResult.isNewEntry)
        return addResult.iterator->value;

    UErrorCode error = U_ZERO_ERROR;
    Vector<UChar, 4> normalizedCharacters(length);
    int32_t normalizedLength = unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], length, &error);
    // Can't render if we have an error or no composition occurred.
    if (U_FAILURE(error) || (static_cast<size_t>(normalizedLength) == length))
        return false;

    FT_Face face = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont());
    if (!face)
        return false;

    // if (FcFreeTypeCharIndex(face, normalizedCharacters[0]))
    if (FT_Get_Char_Index(face, normalizedCharacters[0]))
        addResult.iterator->value = true;

    cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont());
    return addResult.iterator->value;
}
#endif


}
